package com.cyber.parkingremotev2;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Timer;
import java.util.TimerTask;

import com.cyber.parkingremotev2.CommandSystem.Command;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;

public abstract class Communication extends Service {
	Handler mUiHandler;
	SendThread mSendThread;
	RecvThread mRecvThread;
	ComBinder mBinder;
	boolean mContinue;
	CommandSystem mCommandSystem;
	Timer mTimer;
	HeartBeatTask mHeartBeatTask;

	public Communication() {
		mTimer = new Timer();
		mHeartBeatTask = new HeartBeatTask();
		mTimer.scheduleAtFixedRate(mHeartBeatTask, 15000, 15000);
	}

	public void sendData(String data) {
		mSendThread.getSetData(data);
	}

	public void connect(String target) {
		ConThread conThread = new ConThread(target);
		Thread thread = new Thread(conThread);
		thread.start();
	}

	public void setUiHandler(Handler handler) {
		mUiHandler = handler;
	}

	abstract public ArrayList<String> listServers();

	abstract protected void connectServer(String name);

	abstract protected void disconnectServer();

	abstract protected void send(String content);

	abstract protected String recv();

	abstract public String getName();

	protected void sendMessage(int msg_type, String msg_name, String msg_content) {
		if (mUiHandler != null) {
			Bundle bundle = new Bundle();
			bundle.putString(msg_name, msg_content);
			Message msg = mUiHandler.obtainMessage(msg_type);
			msg.setData(bundle);
			mUiHandler.sendMessage(msg);
		}
	}

	protected void sendState(int msg_type, String msg_name, boolean state) {
		if (mUiHandler != null) {
			Bundle bundle = new Bundle();
			bundle.putBoolean(ParkRemoteActivity.M_MOVING, state);
			Message msg = mUiHandler.obtainMessage(msg_type);
			msg.setData(bundle);
			mUiHandler.sendMessage(msg);
		}
	}

	protected void sendStatus(int seq, int distance) {
		if (mUiHandler != null) {
			Bundle bundle = new Bundle();
			bundle.putInt(ParkRemoteActivity.M_STATUS_SEQ, seq);
			bundle.putInt(ParkRemoteActivity.M_STATUS_DISTANCE, distance);
			Message msg = mUiHandler.obtainMessage(ParkRemoteActivity.M_STATUS);
			msg.setData(bundle);
			mUiHandler.sendMessage(msg);
		}
	}

	protected void sendProc(int msg_type, int seq, int type, int value) {
		if (mUiHandler != null) {
			Bundle bundle = new Bundle();
			bundle.putInt(SettingActivity.M_SEQ, seq);
			bundle.putInt(SettingActivity.M_TYPE, type);
			bundle.putInt(SettingActivity.M_VALUE, value);
			Message msg = mUiHandler.obtainMessage(msg_type);
			msg.setData(bundle);
			mUiHandler.sendMessage(msg);
		}
	}

	void processData(String data) {
		mHeartBeatTask.beat();
		ArrayList<CommandSystem.Command> commands;
		commands = mCommandSystem.decode(data);
		for (CommandSystem.Command command : commands) {
			processCommand(command);
		}
	}

	int mAppendDataType = 0;

	void processCommand(CommandSystem.Command com) {
		if (com.seq == 0) {
			switch (com.type) {
			case CommandSystem.CS_PAUSE:
				sendMessage(ParkRemoteActivity.M_STATUS,
						ParkRemoteActivity.M_STATUS_INFO,
						getString(R.string.status_paused));
				break;
			case CommandSystem.CS_MOVE:
				sendMessage(ParkRemoteActivity.M_STATUS,
						ParkRemoteActivity.M_STATUS_INFO,
						getString(R.string.status_parking));
				break;
			case CommandSystem.CS_RESTART:
				sendMessage(ParkRemoteActivity.M_STATUS,
						ParkRemoteActivity.M_STATUS_INFO,
						getString(R.string.status_reset));
				break;
			case CommandSystem.CS_LOOP:
				if (com.value == 0) {
					sendMessage(ParkRemoteActivity.M_MODE,
							ParkRemoteActivity.M_MODE_INFO,
							getString(R.string.mode_manual));
				} else {
					sendMessage(ParkRemoteActivity.M_MODE,
							ParkRemoteActivity.M_MODE_INFO,
							getString(R.string.mode_auto));
				}
				break;
			case CommandSystem.CS_JUMP:
				break;
			case CommandSystem.CS_STATUS:
				break;
			case CommandSystem.CS_STATUS_OFFSET:
				int status = (com.value / 10) % 2;
				int mode = com.value % 2;
				if (status == 0) {
					sendMessage(ParkRemoteActivity.M_STATUS,
							ParkRemoteActivity.M_STATUS_INFO,
							getString(R.string.status_paused));
				} else {
					sendMessage(ParkRemoteActivity.M_STATUS,
							ParkRemoteActivity.M_STATUS_INFO,
							getString(R.string.status_parking));
				}
				if (mode == 0) {
					sendMessage(ParkRemoteActivity.M_MODE,
							ParkRemoteActivity.M_MODE_INFO,
							getString(R.string.mode_manual));
				} else {
					sendMessage(ParkRemoteActivity.M_MODE,
							ParkRemoteActivity.M_MODE_INFO,
							getString(R.string.mode_auto));
				}
				break;
			case CommandSystem.CS_REQUIRE:
				mAppendDataType = CommandSystem.CS_REQUIRE;
				break;
			case CommandSystem.CS_SAVE:
				break;
			case CommandSystem.CS_CONTINUE: {
				sendMessage(ParkRemoteActivity.M_STATUS,
						ParkRemoteActivity.M_STATUS_INFO,
						getString(R.string.continued));
			}
				break;
			default:
				if (com.type > CommandSystem.CS_STATUS_OFFSET
						&& com.type <= CommandSystem.CS_STATUS_OFFSET
								+ CommandSystem.CS_MAX_COMMAND) {
					sendStatus(com.type - CommandSystem.CS_STATUS_OFFSET,
							com.value);
				}
				break;
			}
		} else {
			sendProc(SettingActivity.M_REQUIRE, com.seq, com.type,
					com.value);
//			if (com.type == SettingActivity.getFieldCount()) {
//				mAppendDataType = 0;
//			}
		}

	}

	private class SendThread implements Runnable {
		Queue<String> mDataQueue = new LinkedList<String>();

		public synchronized String getSetData(String data) {
			if (data.equals("")) {
				return mDataQueue.poll();
			} else {
				mDataQueue.add(data);
				return null;
			}
		}

		@Override
		public void run() {
			while (mContinue) {
				String data = getSetData("");
				if (data != null) {
					send(data);
				} else {
					try {
						Thread.sleep(25);
					} catch (InterruptedException e) {
						sendMessage(ParkRemoteActivity.M_UPDATE_TEXT,
								ParkRemoteActivity.M_TEXT,
								e.toString() + e.getMessage());
					}
				}
			}
		}

	}

	private class RecvThread implements Runnable {
		@Override
		public void run() {
			while (mContinue) {
				String data = recv();
				if (data != null) {
					processData(data);
				} else {
					try {
						Thread.sleep(25);
					} catch (InterruptedException e) {
						sendMessage(ParkRemoteActivity.M_UPDATE_TEXT,
								ParkRemoteActivity.M_TEXT,
								e.toString() + e.getMessage());
					}
				}
			}
		}

	}

	private class ConThread implements Runnable {
		public ConThread(String target) {
			mTarget = target;
		}

		String mTarget;

		@Override
		public void run() {
			disconnectServer();
			connectServer(mTarget);
		}

	}

	protected void init() {
		mUiHandler = null;
		mSendThread = new SendThread();
		mRecvThread = new RecvThread();
		mBinder = new ComBinder();
		mCommandSystem = new CommandSystem();
		mContinue = true;
		Thread sendThread = new Thread(mSendThread);
		sendThread.start();
		Thread recvThread = new Thread(mRecvThread);
		recvThread.start();
	}

	public class ComBinder extends Binder {
		public void connect(String target) {
			Communication.this.connect(target);
		}

		public void sendData(String data) {
			Communication.this.sendData(data);
		}

		public void setUiHandler(Handler handler) {
			Communication.this.setUiHandler(handler);
		}

		public String getName() {
			return Communication.this.getName();
		}
	};

	@Override
	public void onCreate() {
		init();
	}

	@Override
	public IBinder onBind(Intent intent) {
		return mBinder;
	}

	private class HeartBeatTask extends TimerTask {
		boolean alive = true;

		public HeartBeatTask() {
			super();
		}

		public void beat() {
			alive = true;
		}

		@Override
		public void run() {
			if (alive) {
				alive = false;
				Communication.this.send(Communication.this.mCommandSystem
						.status());
			} else {
				Communication.this.sendMessage(ParkRemoteActivity.M_CONNECTION,
						ParkRemoteActivity.M_CONNECTION_INFO,
						getString(R.string.disconnected));
			}
		}

	};

}
